#include <stdbool.h>

#define NUM_SUPPLY 10

// Logging functions. Behavior is dependant on -v command line argument, but
// unit test code doesn't need to care about that.
void log_print (void);
void log_flush (void);
void fail_printf (const char *fmt, ...);
void pass_printf (const char *fmt, ...);

// XXX NOTE: These macros require GCC-specific extensions to the C
// preprocessor! (Specifically, using the paste operator on __VA_ARGS__ to 
// prevent double commas when no variadic arguments are supplied.)

#define assert_notop_fromfunc(a,b,op,funcname,msg,...) \
    if (a op b) \
    {\
        fail_printf ("%s: FAIL " msg " (%d " #op " %d!)\n", funcname, ##__VA_ARGS__, a, b); \
        return; \
    }\
    else \
    {\
        pass_printf ("%s: PASS " msg ".\n", funcname, ##__VA_ARGS__); \
    }

#define assert_notequal_fromfunc(a,b,funcname,msg,...) \
    assert_notop_fromfunc (a, b, ==, funcname, msg, ##__VA_ARGS__)

#define assert_equal_fromfunc(a,b,funcname,msg,...) \
    assert_notop_fromfunc (a, b, !=, funcname, msg, ##__VA_ARGS__)

#define assert_notequal(a,b,msg,...) \
    assert_notequal_fromfunc (a, b, __func__, msg, ##__VA_ARGS__)

#define assert_equal(a,b,msg,...) \
    assert_equal_fromfunc (a, b, __func__, msg, ##__VA_ARGS__)


typedef struct
{
    int     count[treasure_map+1];
    int     playedCount[treasure_map+1];
    bool    isOfInterest[treasure_map+1];
} expectedState_t;


// Wrapper around playCard which makes a few assertions for you.
// Don't call this directly, use the testPlayCard macro instead.
bool testPlayCard_fail; // XXX: kind of a hack
void testPlayCard_fromFunc (    int handPos, int choice1, int choice2,
                                int choice3, struct gameState *state,
                                expectedState_t *expected,
                                bool expect_fail, const char *funcname );
#define testPlayCard(handPos,choice1,choice2,choice3,state,expected,expect_fail) \
{\
    testPlayCard_fromFunc ( handPos, choice1, choice2, choice3, state, \
                            expected, expect_fail, __func__ ); \
    if (testPlayCard_fail) \
        return; /* propagate the return */\
}

// Wrapper around endTurn which makes a few assertions for you.
// Don't call this directly, use the testEndTurn macro instead.
bool testEndTurn_fail; // XXX: kind of a hack
void testEndTurn_fromFunc ( struct gameState *state,
                            expectedState_t *expected, const char *funcname );
#define testEndTurn(state,expected) \
{\
    testEndTurn_fromFunc ( state, expected, __func__ ); \
    if (testEndTurn_fail) \
        return; /* propagate the return */\
}


// 4 is the number of gameplay-related arguments to playCard: handPos,
// choice1, choice2, and choice3. We allocate space for 6 so that card types
// and other parameters can be requested without being one of the arguments to
// playCard.
#define MAX_PLAYARGS 6

// Array of phrases of the form "a(n) [card name] in position ##"
#define DESCRIPTION_SZ 64
typedef char carddesc_t[MAX_PLAYARGS][DESCRIPTION_SZ];

// Note the typedef is actually an array of structs. One of these arrays
// describes all the parameters accepted by a given unit test. 
typedef struct
{
    // This enum controls what kind of value is in the parameter.
    enum
    {
        // The parameter is just an integer value.
        // arg_min is the minimum value, arg_max is the maximum value.
        // If playargs_t[n].argtype == arg_num, then
        //  * arg_min <= args[n] <= arg_max
        //  * cardtypes[n] is undefined
        arg_num, 
        
        // The pararameter is a card type ID. Behaves the same as arg_num,
        // except that each card type ID that gets used in the test must be 
        // included in the supply.
        // If playargs_t[n].argtype == arg_cardtype, then
        //  * arg_min <= args[n] <= arg_max
        //  * cardtypes[n] == args[n]
        arg_cardtype,
        
        // The parameter is a position within the player's hand of a card of a
        // given type. Just like with arg_cardtype, arg_min and arg_max are 
        // the minimum and maximum integer values of the card's type ID, but
        // the argument actually given to the test function is a position 
        // within the players hand, which is always in the range of 0 to 
        // handCount-1, inclusive. No two cards can be in the same position.
        // If playargs_t[n].argtype == arg_handcard, then
        //  * 0 <= args[n] <= handCount-1 and
        //      args[n] != args[m] where (m != n and playargs_t[m].argtype == arg_handcard)
        //  * arg_min <= cardtypes[n] <= arg_max
        arg_handcard
    } argtype;
    
    // These two have different meanings depending on the value in argtype.
    int arg_min, arg_max;
} playargs_t[MAX_PLAYARGS]; 

// A pointer to the function that actually does the unit test.
typedef void (*cardtest_f) (    int args[MAX_PLAYARGS],
                                int cardtypes[MAX_PLAYARGS], carddesc_t desc, 
                                struct gameState *g,
                                expectedState_t *expected );

// Enumerate through all valid combinations of parameters within the ranges
// defined by arg_ranges, calling test_callback on each combination.
void run_cardtest (playargs_t arg_ranges, cardtest_f test_callback);

// Set up a test game and gain the "needed cards" into the first player's hand
// in the specified positions within the hand. It's useful to be able to
// specify the positions, because some bugs depend on card position (for
// example, the salvager bug which would stop the salvager card from working 
// when trashing the card in position one.) Use a position of -1 to indicate
// that you don't actually want the card added to the player's hand, you just
// want it in the supply.
typedef struct
{
    int cardtype, position;
} needed_card_t;
void setup_test_game (  int num_needed_cards,
                        const needed_card_t *needed_cards,
                        struct gameState *g, expectedState_t *expected );

// Currently the only argument understood is -v for verbose.
void parse_args (int argc, char *argv[]);

